﻿using System;
using System.Collections.Specialized;
using System.Collections;
using System.Text.RegularExpressions;
using System.Collections.Generic;

namespace CrmWorkflowDependencies.GraphBuilding
{
    /// <summary>
    /// Arguments class
    /// http://www.codeproject.com/Articles/3111/C-NET-Command-Line-Arguments-Parser
    /// </summary>
    class CmdArguments : IEnumerable<string>
    {
        /// <summary>
        /// Словарь для хранения аргументов "ключ-значение"
        /// </summary>
        private Dictionary<string, string> _arguments;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="args"></param>
        public CmdArguments(string[] args)
        {
            _arguments = new Dictionary<string, string>();

            // Define -, --, /, : and = as valid delimiters.  Ignore : and = if enclosed in quotes.
            Regex validDelimiters = new Regex(@"^-{1,2}|^/|[^['""]?.*]=['""]?$|[^['""]?.*]:['""]?$",
                RegexOptions.IgnoreCase | RegexOptions.Compiled);

            // Define anything enclosed with double quotes as a match.  We'll use this to replace
            // the entire string with only the part that matches (everything but the quotes)
            Regex quotedString = new Regex(@"^['""]?(.*?)['""]?$",
                RegexOptions.IgnoreCase | RegexOptions.Compiled);

            string argumentKey = null;
            string[] parts;

            // Valid parameters forms:
            // {-,/,--}param{ ,=,:}((",')value(",'))
            // Examples: 
            // -param1 value1 --param2 /param3:"Test-:-work" 
            //   /param4=happy -param5 '--=nice=--'
            foreach (string arg in args)
            {
                // Apply validDelims to current arg to see how many significant characters were present
                // We're limiting to 3 to forcefully ignore any characters in the parameter VALUE
                // that would normally be used as a delimiter
                parts = validDelimiters.Split(arg, 3);

                switch (parts.Length)
                {
                    // no special characters present.  we assume this means that this part
                    // represents a value to the previously provided parameter.
                    // For example, if we have: "--MyTestArg myValue"
                    // currentParam would currently be set to "--MyTestArg"
                    // parts[0] would hold "myValue", to be assigned to MyTestArg
                    case 1:
                        if (argumentKey != null)
                        {
                            if (!_arguments.ContainsKey(argumentKey))
                            {
                                _arguments.Add(
                                    argumentKey, 
                                    quotedString.Replace(parts[0], "$1"));
                            }

                            argumentKey = null;
                        }
                        // else Error: no parameter waiting for a value (skipped)
                        break;

                    // One split ocurred, meaning we found a parameter delimiter
                    // at the start of arg, but nothing to denote a value.
                    // example: --MyParam
                    case 2:
                        // We already had a parameter with no value last time through the loop.
                        // That means we have no explicit value to give currentParam. We'll default it to "true"
                        if (argumentKey != null && !_arguments.ContainsKey(argumentKey))
                        {
                            _arguments.Add(argumentKey, Boolean.TrueString);
                        }

                        // Store our value-less param and grab the next arg to see if it has our value
                        // parts[0] only contains the opening delimiter -, --, or /, 
                        // so we go after parts[1] for the actual param name
                        argumentKey = parts[1];
                        
                        break;

                    // Two splits occurred.  We found a starting parameter delimiter,
                    // a parameter name, and another delimiter denoting a value for this parameter
                    // Example: --MyParam=MyValue   or   --MyParam:MyValue
                    case 3:
                        // We already had a parameter with no value last time through the loop.
                        // That means we have no explicit value to give currentParam. We'll default it to "true"
                        if (argumentKey != null && !_arguments.ContainsKey(argumentKey))
                        {
                            _arguments.Add(argumentKey, "true");
                        }

                        // Store the good param name
                        argumentKey = parts[1];

                        // Ignores parameters that have already been presented, not thrilled about this approach...
                        if (!_arguments.ContainsKey(argumentKey))
                        {
                            _arguments.Add(argumentKey, quotedString.Replace(parts[2], "$1"));
                        }

                        // Reset currentParam, we already have both parameter and value for this arg
                        argumentKey = null;

                        break;
                }
            }

            // Final cleanup, we may still have a parameter at the end of the args string that didn't get a value
            if (argumentKey != null)
            {
                if (!_arguments.ContainsKey(argumentKey))
                    _arguments.Add(argumentKey, "true");
            }
        }

        // Retrieve a parameter value if it exists 
        // (overriding C# indexer property)
        public string this[string param]
        {
            get
            {
                return (_arguments[param]);
            }
        }

        public bool HasParam(string name)
        {
            return _arguments.ContainsKey(name);
        }

        public IEnumerator<string> GetEnumerator()
        {
            return _arguments.Keys.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return _arguments.Keys.GetEnumerator();
        }
    }
}